#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "symboltable.h"
#include "tags.h"
#include "globals.h"
#include "stack.h"
#include "gc.h"

unsigned char *bytecode;
scm place;

scm bytecode_args[42];
int bytecode_args_num = 0;

int chr_to_hex(unsigned char x) {
  if('0' <= x && x <= '9') {
    return x - '0';
  }
  
  if('A' <= x && x <= 'F') {
    return x - 'A' + 10;
  }

  fprintf(stderr, "chr_to_hex: %c", x);
  exit(-1);
}

scm bytecode_read_n(int n) {
  int i;
  scm res;

  res = 0;

  for(i = 0; i < n; i++)
    res = res * 0x10 + chr_to_hex(bytecode[place++]);
  
  return res;
}

void bytecode_read_place(unsigned char *place_sort, scm *place_index) {
  *place_sort = bytecode[place++];
  switch(*place_sort) {
  case 'r':
    *place_index = 0;
    break;
    
  case 'g':
    *place_index = bytecode_read_n(4);
    break;
    
  case 't':
  case 'l':
  case 'e':
    *place_index = bytecode_read_n(2);
    break;

  default:
    fprintf(stderr, "Unknown place sort %c\n", *place_sort);
    exit(-1);
  }
}

scm bytecode_read_char();
scm bytecode_read_symb();
scm bytecode_read_strn();

scm bytecode_read_val() {
  unsigned char c;

  switch(c = bytecode[place++]) {
  case 'N':
    return atom_tag_null;
    
  case '#':
    return bytecode_read_char();

  case 'x':
    return mk_numb(bytecode_read_n(4));

  case 'X':
    return mk_numb(bytecode_read_n(16));

  case 'S':
    return bytecode_read_symb();

  case '"':
    return bytecode_read_strn();

  case 'r':
    return vm_reg_ret;

  case 'g':
    return vm_global_ref(bytecode_read_n(4));

  case 't':
    return vm_stack_top_ref(bytecode_read_n(2));

  case 'l':
    return vm_stack_base_ref(bytecode_read_n(2));

  case 'e':
    return clos_env_ref(vm_reg_env, bytecode_read_n(2));
    
  default:
    fprintf(stderr, "bytecode_read_val %c", c);
    exit(-1);
  }
}

scm bytecode_read_char() {
  unsigned char c;
  
  switch(c = bytecode[place++]) {
  case 'f':
    return atom_tag_fals;

  case 't':
    return atom_tag_true;

  case '\\':
    c = bytecode[place++];
    return mk_char(c);

  case 'N':
    return mk_char('\n');

  case 'T':
    return mk_char('\t');
    
  default:
    fprintf(stderr, "bytecode_read_read %c", c);
    exit(-1);
  }
}

scm bytecode_read_symb() {
  char symbol_tmp_buf[512];
  int i;
  
  for(i = 0; bytecode[place] != '.'; i++, place++) {
    symbol_tmp_buf[i] = bytecode[place];
    
    if(i >= 512-1) {
      fprintf(stderr, "bytecode_read_symb");
      exit(-1);
    }
  }
  
  symbol_tmp_buf[i] = '\0';
  
  place++;

  return intern(symbol_tmp_buf);
}

void bytecode_read_args() {
  bytecode_args_num = 0;
  while(bytecode[place] != '.') {
    bytecode_args[bytecode_args_num++] = bytecode_read_val();
  }
  place++;
}

scm bytecode_read_strn() {
  char string_tmp_buf[512];
  int i;
  
  for(i = 0; bytecode[place] != '.'; i++, place++) {
    string_tmp_buf[i] = bytecode[place];
  }
  
  string_tmp_buf[i] = '\0';
  
  place++;
  
  return heap_alloc_strn(string_tmp_buf, i);
}
